﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Percolation;
using System.Threading.Tasks;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;
using System.Threading;
using System.Globalization;

namespace Percolation.Gui
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
            Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
            //Thread.CurrentThread.CurrentCulture = new CultureInfo("ru-RU");
            //Thread.CurrentThread.CurrentUICulture = new CultureInfo("ru-RU");

            InitializeComponent();
            InitializePalette();

        }

        private PercolationProject m_project = null;
        private int m_selectedTab = 0;
        private int m_previewPanelDrawMode = 0; // 0 - net, 1 - graphics

        private List<Color> m_palette = new List<Color>();

        private void InitializePalette()
        {
            m_palette.Clear();
            for (int r = 0; r < 256; r += 51)
                for (int g = 0; g < 256; g += 51)
                    for (int b = 0; b < 256; b += 51)
                        m_palette.Add(Color.FromArgb(r, g, b));
        }

        private enum FormStateEnum
        {
            Initial = 0x01,
            ProjectInitial = 0x02,
            ShowProgressBar = 0x04,
            HideProgressBar = 0x08,
            HideForCalculating
        }

        private void updateFormState(FormStateEnum state, object Args = null)
        {
            switch (state)
            {
                case FormStateEnum.Initial:
                    {
                        m_splitContainer.Visible = false;
                        m_toolStripProgressBar.Visible = false;
                        m_projectToolStripSplitButton.Enabled = false;
                        m_fileToolStripSplitButton.DropDownItems[2].Enabled = false;
                        break;
                    }
                case FormStateEnum.ProjectInitial:
                    {
                        m_splitContainer.Visible = true;
                        m_projectToolStripSplitButton.Enabled = true;
                        m_fileToolStripSplitButton.DropDownItems[2].Enabled = true;
                        break;
                    }
                case FormStateEnum.ShowProgressBar:
                    {
                        m_toolStripProgressBar.Visible = true;
                        break;
                    }
                case FormStateEnum.HideProgressBar:
                    {
                        m_toolStripProgressBar.Visible = false;
                        break;
                    }
                case FormStateEnum.HideForCalculating:
                    {
                        m_previewPanel.Invalidate();
                        m_contentToolStripButton1.Visible = false;
                        m_contentToolStripButton2.Visible = false;
                        m_contentToolStripButton3.Visible = false;
                        m_propertiesToolStripContainer.TopToolStripPanelVisible = false;
                        break;
                    }
            }
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            updateFormState(FormStateEnum.Initial);
        }

        private void m_splitContainer1_SplitterMoved(object sender, SplitterEventArgs e)
        {

        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            System.Windows.Forms.Application.Exit();
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            m_openFileDialog.Filter = "Percolation project files (*.ppf)|*.ppf";
            if (m_openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                BinaryFormatter formatter = new BinaryFormatter();
                using (FileStream file = new FileStream(m_openFileDialog.FileName, FileMode.Open))
                {
                    m_project = (PercolationProject)formatter.Deserialize(file);
                    updateTree();
                    m_treeView.ExpandAll();
                    updateFormState(FormStateEnum.ProjectInitial);
                }
            }
        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (m_project != null)
                return;

            m_project = new PercolationProject("Percolation project");
            updateTree();
            updateFormState(FormStateEnum.ProjectInitial);
        }

        private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (m_project == null)
                throw new ApplicationException("Internal error");

            ProjectSettingsForm dialog = new ProjectSettingsForm(m_project.Settings);
            dialog.ShowDialog();
        }

        private void updateTree()
        {
            if (m_project == null)
                return;

            m_treeView.Nodes.Clear();
            m_treeView.Nodes.Add(m_project.Name);
            m_treeView.Nodes[0].setIcon(TreeNodeIcon.Project);
            m_treeView.Nodes[0].Tag = new TreeNodeTag(m_project);

            int rangeCounter = 0;
            foreach (ExperimentsRange range in m_project.Ranges)
            {
                TreeNode rangeNode = new TreeNode(range.GetDescription());
                rangeNode.Tag = new TreeNodeTag(m_project, rangeCounter);
                rangeNode.setIcon(rangeNode.getTag().GetRange().isProcessed() ? TreeNodeIcon.Processed : TreeNodeIcon.NotProcessed);
                int packCounter = 0;
                foreach (ExperimentsPack pack in range.Packs)
                {
                    TreeNode packNode = new TreeNode(pack.GetDescription());
                    packNode.Tag = new TreeNodeTag(m_project, rangeCounter, packCounter);
                    packNode.setIcon(packNode.getTag().GetPack().isProcessed() ? TreeNodeIcon.Processed : TreeNodeIcon.NotProcessed);
                    int expCounter = 0;
                    foreach (Experiment exp in pack.Experiments)
                    {
                        TreeNode expNode = new TreeNode("Experiment " + (expCounter + 1).ToString());
                        expNode.Tag = new TreeNodeTag(m_project, rangeCounter, packCounter, expCounter);
                        expNode.setIcon(expNode.getTag().GetExperiment().isProcessed() ? TreeNodeIcon.Processed : TreeNodeIcon.NotProcessed);
                        ++expCounter;
                        packNode.Nodes.Add(expNode);
                    }

                    ++packCounter;
                    rangeNode.Nodes.Add(packNode);
                }

                ++rangeCounter;
                m_treeView.Nodes[0].Nodes.Add(rangeNode);
            }

            m_treeView.ExpandAll();
        }

        private void m_addPackToolStripButton_Click(object sender, EventArgs e)
        {
            if (m_project == null)
                throw new ApplicationException("Internal error");

            EditRangeForm dialog = new EditRangeForm(new NetRangeSettings());
            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                m_project.Ranges.Add(new ExperimentsRange(dialog.RangeSettings));
                updateTree();
            }

        }

        private void m_removePackToolStripButton_Click(object sender, EventArgs e)
        {
            if (m_project == null)
                return;
            
            TreeNode selectedNode = m_treeView.SelectedNode;
            if (selectedNode == null)
                return;

            TreeNodeTag tag = (TreeNodeTag)selectedNode.Tag;

            if (tag.IsRange)
            {
                if (MessageBox.Show("Delete range?", "Percolation app", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes)
                {
                    m_project.Ranges.RemoveAt(tag.RangeNum);
                    updateTree();
                }
            }
        }

        public enum TreeNodeIcon
        {
            Project = 0,
            NotProcessed = 1,
            Processing = 2,
            Processed = 3
        }

        public enum TreeNodeState
        {
            NotProcessed,
            Processing,
            Processed
        }

        public class TreeNodeTag
        {
            public int RangeNum { get; private set; }
            public int PackNum { get; private set; }
            public int ExpNum { get; private set; }
            public bool IsRoot { get; private set; }
            public bool IsRange { get; private set; }
            public bool IsPack { get; private set; }
            public bool IsExperiment { get; private set; }

            protected PercolationProject m_project;

            protected TreeNodeTag(PercolationProject project, bool isRoot, int rangeNum, int packNum, int expNum)
            {
                m_project = project;

                RangeNum = rangeNum;
                PackNum = packNum;
                ExpNum = expNum;

                IsRoot = isRoot;
                IsRange = false;
                IsPack = false;
                IsExperiment = false;
            }

            public TreeNodeTag(PercolationProject project)
                : this(project, true, -1, -1, -1)
            {
            }

            public TreeNodeTag(PercolationProject project, int rangeNum)
                : this(project, false, rangeNum, -1, -1)
            {
                IsRange = true;
            }

            public TreeNodeTag(PercolationProject project, int rangeNum, int packNum)
                : this(project, false, rangeNum, packNum, -1)
            {
                IsPack = true;
            }

            public TreeNodeTag(PercolationProject project, int rangeNum, int packNum, int expNum)
                : this(project, false, rangeNum, packNum, expNum)
            {
                IsExperiment = true;
            }

            public ExperimentsRange GetRange()
            {
                if (!IsRange)
                    throw new ApplicationException("Internal error!");

                return m_project.Ranges[RangeNum];
            }

            public ExperimentsPack GetPack()
            {
                if (!IsPack)
                    throw new ApplicationException("Internal error!");

                return m_project.Ranges[RangeNum].Packs[PackNum];
            }

            public Experiment GetExperiment()
            {
                if (!IsExperiment)
                    throw new ApplicationException("Internal error!");

                return m_project.Ranges[RangeNum].Packs[PackNum].Experiments[ExpNum];
            }
        }

        private NetInterface getNetForDraw()
        {
            if (m_project == null)
                return null;
            
            TreeNode node = m_treeView.SelectedNode;
            if (node == null)
                return null;

            TreeNodeTag tag = node.getTag();

            if (tag.IsExperiment)
                return tag.GetExperiment().Net;
            
            return null;
        }

        private Graphic getGraphicForDraw()
        {
            if (m_project == null)
                return null;

            TreeNode node = m_treeView.SelectedNode;
            if (node == null)
                return null;

            TreeNodeTag tag = node.getTag();
            if (tag.IsRoot && m_propertiesToolStripComboBox.SelectedIndex >= 0 && m_propertiesToolStripComboBox.SelectedIndex < m_project.Graphics.Count)
                return m_project.Graphics[m_propertiesToolStripComboBox.SelectedIndex];

            return null;
        }

        private NetInterface getClusterSchemeForDraw()
        {
            if (m_project == null)
                return null;
            
            TreeNode selectedNode = m_treeView.SelectedNode;
            if (selectedNode == null)
                return null;

            TreeNodeTag tag = (TreeNodeTag)selectedNode.Tag;

            if (tag.IsExperiment)
            {
                if (tag.GetExperiment().HKStats != null)
                    return tag.GetExperiment().HKStats.ResultNet;
            }
            
            return null;
        }

        private void updateContentPane()
        {
            TreeNode node = m_treeView.SelectedNode;
            if (node == null)
                return;

            TreeNodeTag tag = node.getTag();

            if (tag.IsExperiment)
            {
                m_previewPanelDrawMode = 0;

                Experiment exp = tag.GetExperiment();
                m_contentToolStripButton1.Text = "  Net  ";
                m_contentToolStripButton1.Visible = true;
                m_contentToolStripButton1.Checked = (m_selectedTab == 0);
                m_contentToolStripButton2.Text = "Hoshen-Kopelman";
                m_contentToolStripButton2.Visible = true;
                m_contentToolStripButton2.Enabled = (exp.HKStats != null);
                m_contentToolStripButton2.Checked = (m_selectedTab == 1);
                m_contentToolStripButton3.Text = "  Bolt  ";
                m_contentToolStripButton3.Visible = true;
                m_contentToolStripButton3.Enabled = (exp.BoltStats != null);
                m_contentToolStripButton3.Checked = (m_selectedTab == 2);

                if (m_selectedTab == 2)
                {
                    m_propertiesToolStripContainer.TopToolStripPanelVisible = true;
                    m_propertiesToolStripComboBox.Visible = true;

                    if (exp.BoltStats != null)
                    {
                        m_propertiesToolStripComboBox.Items.Clear();
                        int counter = 1;
                        foreach (var path in exp.BoltStats.PathStatisticsList)
                            m_propertiesToolStripComboBox.Items.Add("Path " + counter++.ToString());

                        m_propertiesToolStripComboBox.SelectedIndex = 0;
                    }

                    m_actionsToolStripSplitButton.Visible = false;
                    m_exportToolStripButton.Visible = false;
                    m_saveToolStripButton.Visible = true;
                }
                else
                {
                    m_propertiesToolStripContainer.TopToolStripPanelVisible = true;
                    m_propertiesToolStripComboBox.Visible = false;
                    m_actionsToolStripSplitButton.Visible = false;
                    m_exportToolStripButton.Visible = false;
                    m_saveToolStripButton.Visible = true;
                    m_propertiesToolStripComboBox.SelectedIndex = -1;
                }

                m_statsDataGridView.Visible = false;
                m_propertiesDataGridView.Visible = true;
            }
            else if (tag.IsPack || tag.IsRange || tag.IsRoot)
            {
                string buttonCaption = "";
                if (tag.IsPack)
                {
                    buttonCaption = "Pack statistics";
                    m_statsDataGridView.Visible = true;
                    m_propertiesToolStripContainer.TopToolStripPanelVisible = true;
                    m_propertiesToolStripComboBox.Visible = false;
                    m_actionsToolStripSplitButton.Visible = false;
                    m_exportToolStripButton.Visible = tag.GetPack().PackStats != null;
                    m_saveToolStripButton.Visible = false;
                }
                else if (tag.IsRange)
                {
                    buttonCaption = "Range statistics";
                    m_statsDataGridView.Visible = true;
                    m_propertiesToolStripContainer.TopToolStripPanelVisible = true;
                    m_propertiesToolStripComboBox.Visible = false;
                    m_actionsToolStripSplitButton.Visible = false;
                    m_exportToolStripButton.Visible = tag.GetRange().Packs.TrueForAll(p => p.PackStats != null);
                    m_saveToolStripButton.Visible = false;
                }
                else
                {
                    m_previewPanelDrawMode = 0;
                    buttonCaption = "  Graphics  ";
                    m_statsDataGridView.Visible = false;
                    m_propertiesToolStripContainer.TopToolStripPanelVisible = true;

                    bool showControls = m_project.Ranges.TrueForAll(
                        x => x.Packs.TrueForAll(p => p.PackStats != null)
                    );

                    m_propertiesToolStripComboBox.Visible = showControls;
                    m_actionsToolStripSplitButton.Visible = showControls;
                    m_exportToolStripButton.Visible = false;
                    m_saveToolStripButton.Visible = false;

                    m_propertiesToolStripComboBox.Items.Clear();
                    int counter = 1;
                    foreach (Graphic g in m_project.Graphics)
                        m_propertiesToolStripComboBox.Items.Add("Graphic " + counter++.ToString());

                    if (m_project.Graphics.Count > 0)
                        m_propertiesToolStripComboBox.SelectedIndex = 0;
                }

                m_contentToolStripButton1.Text = buttonCaption;
                m_contentToolStripButton1.Visible = true;
                m_contentToolStripButton1.Checked = true;
                m_selectedTab = 0;

                m_contentToolStripButton2.Text = "";
                m_contentToolStripButton2.Visible = false;
                m_contentToolStripButton3.Text = "";
                m_contentToolStripButton3.Visible = false;

                m_propertiesDataGridView.Visible = false;
            }
        }

        private void updateTreeViewControls()
        {
            TreeNode node = m_treeView.SelectedNode;
            if (node == null)
                return;

            TreeNodeTag tag = node.getTag();

            m_calcToolStripSplitButton.DropDownItems[3].Enabled = true;
            m_calcToolStripSplitButton.DropDownItems[0].Enabled = tag.IsExperiment;
            m_calcToolStripSplitButton.DropDownItems[1].Enabled = tag.IsPack;
            m_calcToolStripSplitButton.DropDownItems[2].Enabled = tag.IsRange;

            m_removeRangeToolStripButton.Enabled = tag.IsRange;
        }

        private void updateNetStats(NetStatistics stats)
        {
            m_propertiesDataGridView.Columns.Clear();
            m_propertiesDataGridView.Columns.Add("Stat", "");
            m_propertiesDataGridView.Columns.Add("Val", "");
            m_propertiesDataGridView.Columns[0].SortMode = DataGridViewColumnSortMode.NotSortable;
            m_propertiesDataGridView.Columns[1].SortMode = DataGridViewColumnSortMode.NotSortable;

            m_propertiesDataGridView.Rows.Clear();
            m_propertiesDataGridView.Rows.Add("Avg black/row", stats.AvgBlackCellRow.ToString("F3"));
            m_propertiesDataGridView.Rows.Add("Avg black/column", stats.AvgBlackCellColumn.ToString("F3"));
            m_propertiesDataGridView.Rows.Add("Null rows", stats.NullRowCount.ToString());
            m_propertiesDataGridView.Rows.Add("Null columns", stats.NullColumnCount.ToString());
            m_propertiesDataGridView.Rows.Add("Black cells", stats.BlackCellCount.ToString());
        }

        private void updateHKStats(HKStatistics stats)
        {
            m_propertiesDataGridView.Columns.Clear();
            m_propertiesDataGridView.Columns.Add("Stat", "");
            m_propertiesDataGridView.Columns.Add("Val", "");
            m_propertiesDataGridView.Columns[0].SortMode = DataGridViewColumnSortMode.NotSortable;
            m_propertiesDataGridView.Columns[1].SortMode = DataGridViewColumnSortMode.NotSortable;

            m_propertiesDataGridView.Rows.Clear();
            m_propertiesDataGridView.Rows.Add("Clstr count", stats.ClusterCount.ToString());
            m_propertiesDataGridView.Rows.Add("Avg clstr size", stats.ClusterAvgSize.ToString("F3"));
            m_propertiesDataGridView.Rows.Add("Max clstr size", stats.ClusterMaxSize.ToString());
            m_propertiesDataGridView.Rows.Add("Min clstr size", stats.ClusterMinSize.ToString());
            m_propertiesDataGridView.Rows.Add("Percolation", stats.Percolation ? "true" : "false");
            if (stats.Percolation)
                m_propertiesDataGridView.Rows.Add("Percolation clstr size", stats.PercolationClusterSize.ToString());
        }

        private void updateBoltStats(BoltStatistics stats)
        {
            int pathNum = m_propertiesToolStripComboBox.SelectedIndex;
            if (0 <= pathNum && pathNum < stats.PathStatisticsList.Count)
            {
                m_propertiesDataGridView.Columns.Clear();
                m_propertiesDataGridView.Columns.Add("Stat", "");
                m_propertiesDataGridView.Columns.Add("Val", "");
                m_propertiesDataGridView.Columns[0].SortMode = DataGridViewColumnSortMode.NotSortable;
                m_propertiesDataGridView.Columns[1].SortMode = DataGridViewColumnSortMode.NotSortable;

                NetPathStatistics pathStats = stats.PathStatisticsList[pathNum].Value;

                m_propertiesDataGridView.Rows.Clear();
                m_propertiesDataGridView.Rows.Add("Added cells", pathStats.AddedCellsCount.ToString());
                m_propertiesDataGridView.Rows.Add("Route length", pathStats.RouteLength.ToString());
                m_propertiesDataGridView.Rows.Add("Avg route/clusters", pathStats.AvgRouteClusters.ToString("F3"));
                m_propertiesDataGridView.Rows.Add("Max route/clusters", pathStats.MaxRouteClusters.ToString());
                m_propertiesDataGridView.Rows.Add("Added cells concentration", pathStats.AddedCellsConcentration.ToString("F3"));
            }
        }

        private void updatePackStats(PackStatistics stats)
        {
            m_statsDataGridView.Columns.Clear();
            m_statsDataGridView.Columns.Add("Stat", "");
            m_statsDataGridView.Columns.Add("Val", "");
            m_statsDataGridView.Columns[0].SortMode = DataGridViewColumnSortMode.NotSortable;
            m_statsDataGridView.Columns[1].SortMode = DataGridViewColumnSortMode.NotSortable;

            m_statsDataGridView.Rows.Clear();
            m_statsDataGridView.Rows.Add("Average black cells in row", stats.NetAvgBlackCellRow.ToString("F3"));
            m_statsDataGridView.Rows.Add("Average black cells in column", stats.NetAvgBlackCellColumn.ToString("F3"));
            m_statsDataGridView.Rows.Add("Average null rows", stats.NetAvgNullRowCount.ToString("F3"));
            m_statsDataGridView.Rows.Add("Average null columns", stats.NetAvgNullColumnCount.ToString("F3"));
            m_statsDataGridView.Rows.Add("Average black cells", stats.NetAvgBlackCellCount.ToString("F3"));
            m_statsDataGridView.Rows.Add("Average cluster count", stats.HKAvgClusterCount.ToString("F3"));
            m_statsDataGridView.Rows.Add("Average cluster size", stats.HKAvgClusterSize.ToString("F3"));
            m_statsDataGridView.Rows.Add("Percolation probability", stats.HKPercolationProbability.ToString("F3"));
            m_statsDataGridView.Rows.Add("Average percolation cluster size", stats.HKAvgPercolationClusterSize.ToString("F3"));
            m_statsDataGridView.Rows.Add("Average added cells", stats.BoltAvgAddedCellCount.ToString("F3"));
            m_statsDataGridView.Rows.Add("Average route length", stats.BoltAvgRouteLength.ToString("F3"));
            m_statsDataGridView.Rows.Add("Average route between clusters", stats.BoltAvgRouteClusters.ToString("F3"));
            m_statsDataGridView.Rows.Add("Max route between clusters", stats.BoltMaxRouteClusters.ToString());
            m_statsDataGridView.Rows.Add("Added cells concentration", stats.BoltAddedCellsConcentration.ToString("F3"));
        }

        private void updateRangeStats(PackStatistics[] statsArray, String [] captionArray)
        {
            m_statsDataGridView.Columns.Clear();
            m_statsDataGridView.Columns.Add("Stat", "");
            foreach (String s in captionArray)
                m_statsDataGridView.Columns.Add("val_" + s, s);

            for (int i = 0; i < m_statsDataGridView.Columns.Count; ++i )
                m_statsDataGridView.Columns[i].SortMode = DataGridViewColumnSortMode.NotSortable;

            m_statsDataGridView.Rows.Clear();
            m_statsDataGridView.Rows.Add(14);
            m_statsDataGridView.Rows[0].Cells[0].Value = "Average black cells in row";
            m_statsDataGridView.Rows[1].Cells[0].Value = "Average black cells in column";
            m_statsDataGridView.Rows[2].Cells[0].Value = "Average null rows";
            m_statsDataGridView.Rows[3].Cells[0].Value = "Average null columns";
            m_statsDataGridView.Rows[4].Cells[0].Value = "Average black cells";
            m_statsDataGridView.Rows[5].Cells[0].Value = "Average cluster count";
            m_statsDataGridView.Rows[6].Cells[0].Value = "Average cluster size";
            m_statsDataGridView.Rows[7].Cells[0].Value = "Percolation probability";
            m_statsDataGridView.Rows[8].Cells[0].Value = "Average percolation cluster size";
            m_statsDataGridView.Rows[9].Cells[0].Value = "Average added cells";
            m_statsDataGridView.Rows[10].Cells[0].Value = "Average route length";
            m_statsDataGridView.Rows[11].Cells[0].Value = "Average route between clusters";
            m_statsDataGridView.Rows[12].Cells[0].Value = "Max route between clusters";
            m_statsDataGridView.Rows[13].Cells[0].Value = "Added cells concentration";

            int cell = 1;
            foreach (PackStatistics stats in statsArray)
            {
                m_statsDataGridView.Rows[0].Cells[cell].Value = stats.NetAvgBlackCellRow.ToString("F3");
                m_statsDataGridView.Rows[1].Cells[cell].Value = stats.NetAvgBlackCellColumn.ToString("F3");
                m_statsDataGridView.Rows[2].Cells[cell].Value = stats.NetAvgNullRowCount.ToString("F3");
                m_statsDataGridView.Rows[3].Cells[cell].Value = stats.NetAvgNullColumnCount.ToString("F3");
                m_statsDataGridView.Rows[4].Cells[cell].Value = stats.NetAvgBlackCellCount.ToString("F3");
                m_statsDataGridView.Rows[5].Cells[cell].Value = stats.HKAvgClusterCount.ToString("F3");
                m_statsDataGridView.Rows[6].Cells[cell].Value = stats.HKAvgClusterSize.ToString("F3");
                m_statsDataGridView.Rows[7].Cells[cell].Value = stats.HKPercolationProbability.ToString("F3");
                m_statsDataGridView.Rows[8].Cells[cell].Value = stats.HKAvgPercolationClusterSize.ToString("F3");
                m_statsDataGridView.Rows[9].Cells[cell].Value = stats.BoltAvgAddedCellCount.ToString("F3");
                m_statsDataGridView.Rows[10].Cells[cell].Value = stats.BoltAvgRouteLength.ToString("F3");
                m_statsDataGridView.Rows[11].Cells[cell].Value = stats.BoltAvgRouteClusters.ToString("F3");
                m_statsDataGridView.Rows[12].Cells[cell].Value = stats.BoltMaxRouteClusters.ToString();
                m_statsDataGridView.Rows[13].Cells[cell].Value = stats.BoltAddedCellsConcentration.ToString("F3");

                ++cell;
            }

        }

        private void m_treeView_AfterSelect(object sender, TreeViewEventArgs e)
        {   
            if (m_project == null)
                return;
            TreeNode selectedNode = m_treeView.SelectedNode;
            if (selectedNode == null)
                return;

            updateTreeViewControls();
            updateContentPane();
            m_previewPanel.Invalidate();

            TreeNodeTag tag = (TreeNodeTag)selectedNode.Tag;
            if (tag.IsPack)
            {
                if (tag.GetPack().PackStats != null)
                    updatePackStats(tag.GetPack().PackStats);
            }

            if (tag.IsRange)
            {
                PackStatistics [] statsArray = new PackStatistics[tag.GetRange().Packs.Count];
                String[] captionArray = new String[tag.GetRange().Packs.Count];
                bool canShowStats = true;
                for (int i = 0; i < tag.GetRange().Packs.Count; ++i)
                {
                    if (tag.GetRange().Packs[i].PackStats != null)
                    {
                        statsArray[i] = tag.GetRange().Packs[i].PackStats;
                        captionArray[i] = tag.GetRange().Packs[i].GetShortDescription();
                    }
                    else
                    {
                        canShowStats = false;
                        break;
                    }
                }

                if (canShowStats)
                    updateRangeStats(statsArray, captionArray);
            }

            if (!tag.IsExperiment)
                return;

            Experiment experiment = tag.GetExperiment();

            if (m_selectedTab == 0 && experiment.NetStats != null)
                updateNetStats(experiment.NetStats);
            else if (m_selectedTab == 1 && experiment.HKStats != null)
                updateHKStats(experiment.HKStats);
            else if (m_selectedTab == 2 && experiment.BoltStats != null)
                updateBoltStats(experiment.BoltStats);
        }

        private void drawGraphic(Graphics g, Graphic graphic, int pixelWidth, int pixelHeight)
        {
            g.FillRectangle(new SolidBrush(Color.White), 0, 0, pixelWidth, pixelHeight);
            
            int leftMargin = 50;
            int rightMargin = 20;
            int topMargin = 20;
            int bottomMargin = 50;
            int canvasGapX = 15;
            int canvasGapY = 15;

            List<SortedDictionary<double, double>> pointsArray = new List<SortedDictionary<double, double>>(graphic.RangeNums.Count);

            foreach (int rangeNum in graphic.RangeNums)
            {
                ExperimentsRange range = m_project.Ranges[rangeNum];
                SortedDictionary<double, double> points = new SortedDictionary<double, double>();
                points.Add(graphic.getStartX(rangeNum), graphic.getStartY(rangeNum));
                points.Add(graphic.getEndX(rangeNum), graphic.getEndY(rangeNum));
                foreach (ExperimentsPack pack in range.Packs)
                {
                    double val = 0.0;
                    switch (graphic.Type)
                    {
                        case GraphicTypeEnum.AvgBlackCellsInRowByImpurity:
                            val = pack.PackStats.NetAvgBlackCellRow; break;
                        case GraphicTypeEnum.AvgBlackCellsInColumnByImpurity:
                            val = pack.PackStats.NetAvgBlackCellColumn; break;
                        case GraphicTypeEnum.AvgNullRowCountByImpurity:
                            val = pack.PackStats.NetAvgNullRowCount; break;
                        case GraphicTypeEnum.AvgNullColumnCountByImpurity:
                            val = pack.PackStats.NetAvgNullColumnCount; break;
                        case GraphicTypeEnum.AvgBlackCellsCountByImpurity:
                            val = pack.PackStats.NetAvgBlackCellCount; break;
                        case GraphicTypeEnum.AvgClusterCountByImpurity:
                            val = pack.PackStats.HKAvgClusterCount; break;
                        case GraphicTypeEnum.AvgClusterSizeByImpurity:
                            val = pack.PackStats.HKAvgClusterSize; break;
                        case GraphicTypeEnum.PercolationProbabilityByImpurity:
                            val = pack.PackStats.HKPercolationProbability; break;
                        case GraphicTypeEnum.AvgPercolationClusterSizeByImpurity:
                            val = pack.PackStats.HKAvgPercolationClusterSize; break;
                        case GraphicTypeEnum.AvgAddedCellsCountByImpurity:
                            val = pack.PackStats.BoltAvgAddedCellCount; break;
                        case GraphicTypeEnum.AvgRouteLengthByImpurity:
                            val = pack.PackStats.BoltAvgRouteLength; break;
                        case GraphicTypeEnum.AvgRouteClustersByImpurity:
                            val = pack.PackStats.BoltAvgRouteClusters; break;
                        case GraphicTypeEnum.MaxRouteClustersByImpurity:
                            val = pack.PackStats.BoltMaxRouteClusters; break;
                        case GraphicTypeEnum.AvgAddedCellsConcentrationByImpurity:
                            val = pack.PackStats.BoltAddedCellsConcentration; break;
                        default:
                            break;;
                    }

                    points.Add(pack.Settings.Probability0, val);
                }

                pointsArray.Add(points);
            }

            double minX = 0.0;
            double maxX = pointsArray.Select(x => x.Keys.Max()).Max();
            double minY = 0.0;
            double maxY = pointsArray.Select(x => x.Values.Max()).Max();

            System.Drawing.Font font = new System.Drawing.Font("Calibri", 10);

            if (g.MeasureString(maxY.ToString("F2"), font).Width + 10 > leftMargin)
                leftMargin = System.Convert.ToInt32(g.MeasureString(maxY.ToString("F2"), font).Width) + 10;

            Brush brush = new SolidBrush(Color.Black);
            Pen axisPen = new Pen(brush);

            g.DrawLine(axisPen, leftMargin, pixelHeight - bottomMargin,
                pixelWidth - rightMargin, pixelHeight - bottomMargin);
            g.DrawLine(axisPen, pixelWidth - rightMargin, pixelHeight - bottomMargin,
                pixelWidth - rightMargin - 5, pixelHeight - bottomMargin - 5);
            g.DrawLine(axisPen, pixelWidth - rightMargin, pixelHeight - bottomMargin,
                pixelWidth - rightMargin - 5, pixelHeight - bottomMargin + 5);

            g.DrawLine(axisPen, leftMargin, topMargin,
                leftMargin, pixelHeight - bottomMargin);
            g.DrawLine(axisPen, leftMargin, topMargin,
                leftMargin - 5, topMargin + 5);
            g.DrawLine(axisPen, leftMargin, topMargin,
                leftMargin + 5, topMargin + 5);

            g.DrawString(graphic.getCaptionX(), font, brush, pixelWidth - rightMargin + 5, pixelHeight - bottomMargin - 5);
            g.DrawString(graphic.getCaptionY(), font, brush, leftMargin, topMargin - 5 - 15);

            Color[] colorArray = { Color.Blue, Color.Red, Color.Green, Color.Orange, Color.Violet, Color.Azure };
            int colorNum = 0;
            int captionStart = 5;
            int captionGap = 20;

            int pointsNum = 0;
            foreach (int rangeNum in graphic.RangeNums)
            {
                ExperimentsRange range = m_project.Ranges[rangeNum];
                
                SizeF size = g.MeasureString(range.GetShortDescription(), font);
                g.FillRectangle(new SolidBrush(colorArray[colorNum]), captionStart, pixelHeight - size.Height, 15, 10);
                g.DrawString(range.GetShortDescription(), font, brush, captionStart + 20, pixelHeight - size.Height);
                captionStart += System.Convert.ToInt32(size.Width) + 5 + captionGap;

                double canvasWidth = pixelWidth - leftMargin - rightMargin - canvasGapX;
                double canvasHeight = pixelHeight - topMargin - bottomMargin - canvasGapY;

                double scaleX = canvasWidth / (maxX - minX);
                double scaleY = canvasHeight / (maxY - minY);

                for (int i = 0; i < graphic.getCaptionXCount(); ++i)
                {
                    double val = (maxX - minX) / (graphic.getCaptionXCount() - 1) * i + minX;
                    float x = (float)(val * scaleX) + leftMargin;
                    g.DrawLine(axisPen, x, pixelHeight - bottomMargin, x, pixelHeight - bottomMargin + 5);

                    SizeF textSize = g.MeasureString(val.ToString("F2"), font);
                    g.DrawString(val.ToString("F2"), font, brush, x - textSize.Width / 2, pixelHeight - bottomMargin + 5);
                }

                for (int i = 0; i < graphic.getCaptionYCount(); ++i)
                {
                    double val = (maxY - minY) / (graphic.getCaptionYCount() - 1) * i + minY;
                    float y = - (float)(val * scaleY) + pixelHeight - bottomMargin;
                    g.DrawLine(axisPen, leftMargin, y, leftMargin - 5, y);

                    SizeF textSize = g.MeasureString(val.ToString("F2"), font);
                    g.DrawString(val.ToString("F2"), font, brush, leftMargin - textSize.Width - 5, y - textSize.Height/2);
                }

                bool first = true;
                KeyValuePair<double, double> previous = new KeyValuePair<double, double>();
                Pen zigzagPen = new Pen(colorArray[colorNum++]);
                if (colorNum == colorArray.Count())
                    colorNum = 0;

                foreach (KeyValuePair<double, double> p in pointsArray[pointsNum++])
                {
                    if (first)
                    {
                        previous = p;
                        first = false;
                    }
                    else
                    {
                        PointF p1 = new PointF((float)(previous.Key * scaleX) + leftMargin, - (float)(previous.Value * scaleY) + pixelHeight - bottomMargin);
                        PointF p2 = new PointF((float)(p.Key * scaleX) + leftMargin, -(float)(p.Value * scaleY) + pixelHeight - bottomMargin);
                        g.DrawLine(zigzagPen, p1, p2);

                        previous = p;
                    }
                }
            }
        }

        private void drawNet(Graphics g, NetInterface net, int pixelWidth, int pixelHeight, NetPath netPath, NetInterface clusterScheme)
        {            
            Bitmap bitmap = new Bitmap(pixelWidth, pixelHeight, PixelFormat.Format24bppRgb);
            BitmapData bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, pixelWidth, pixelHeight), 
                System.Drawing.Imaging.ImageLockMode.ReadWrite, 
                bitmap.PixelFormat);

            int numBytes = bitmapData.Stride * bitmap.Height;
            byte [] buffer = new byte[numBytes];

            Marshal.Copy(bitmapData.Scan0, buffer, 0, numBytes);

            setBitmapRect(0, 0, bitmap.Width, bitmap.Height, bitmapData.Stride, System.Drawing.SystemColors.ControlDark, buffer);

            int cellWidth = (pixelWidth - 4) / net.getWidth();
            int cellHeight = (pixelHeight - 4) / net.getHeight();

            int startX = (pixelWidth - cellWidth * net.getWidth()) / 2;
            int startY = (pixelHeight - cellHeight * net.getHeight()) / 2;
            
            if (m_selectedTab == 0 || m_selectedTab == 2)
            {
                for (int i = 0; i < net.getWidth(); ++i)
                {
                    for (int j = 0; j < net.getHeight(); ++j)
                    {
                        Color cl = Color.Empty;
                        int cellValue = net.getValue(i, j);
                        if (cellValue == NetCellColor.Black)
                            cl = Color.Black;
                        else if (cellValue == NetCellColor.White)
                            cl = Color.White;

                        setBitmapRect(startX + i * cellWidth, startY + j * cellHeight, cellWidth, cellHeight, Math.Abs(bitmapData.Stride), cl, buffer);
                    }
                }

                if (netPath != null)
                {
                    foreach (NetPath.Cell cell in netPath.Cells)
                    {
                        Color cl = (cell.Color == NetCellColor.Black) ? Color.DarkRed : Color.Red;
                        setBitmapRect(startX + cell.X * cellWidth, startY + cell.Y * cellHeight, cellWidth, cellHeight, Math.Abs(bitmapData.Stride), cl, buffer);
                    }
                }
            }
            else if (m_selectedTab == 1 && clusterScheme != null)
            {
                for (int i = 0; i < clusterScheme.getWidth(); ++i)
                {
                    for (int j = 0; j < clusterScheme.getHeight(); ++j)
                    {
                        Color cl = Color.Empty;
                        int cellValue = clusterScheme.getValue(i, j);

                        if (cellValue == 0)
                            cl = m_palette[215];
                        else
                            cl = m_palette[cellValue % 215];

                        setBitmapRect(startX + i * cellWidth, startY + j * cellHeight, cellWidth, cellHeight, Math.Abs(bitmapData.Stride), cl, buffer);    
                    }
                }
            }

            Marshal.Copy(buffer, 0, bitmapData.Scan0, numBytes);
            bitmap.UnlockBits(bitmapData);

            g.DrawImage(bitmap, new System.Drawing.Point(0, 0));
        }

        private void setBitmapRect(int x0, int y0, int rectWidth, int rectHeight, int stride, Color c, byte[] buffer)
        {
            for (int j = y0; j < y0 + rectHeight; ++j)
            {
                int offset = j * stride + 3 * x0;
                for (int i = x0; i < x0 + rectWidth; ++i)
                {
                    buffer[offset++] = c.B;
                    buffer[offset++] = c.G;
                    buffer[offset++] = c.R;
                }
            }
        }

        private void setBitmapPoint(int x, int y, int stride, Color c, byte [] buffer)
        {
            int offset = y * stride + 3 * x;
            buffer[offset + 0] = c.B;
            buffer[offset + 1] = c.G;
            buffer[offset + 2] = c.R;
        }

        private void m_previewPanel_Paint(object sender, PaintEventArgs e)
        {
            if (m_previewPanelDrawMode == 0)
            {
                NetInterface net = getNetForDraw();
                if (net == null)
                    return;

                drawNet(e.Graphics, net, m_previewPanel.Width, m_previewPanel.Height, getNetPathForDraw().Key,
                    getClusterSchemeForDraw());
            }
            else if (m_previewPanelDrawMode == 1)
            {
                Graphic graphic = getGraphicForDraw();
                if (graphic == null)
                    return;

                drawGraphic(e.Graphics, graphic, m_previewPanel.Width, m_previewPanel.Height);
            }
        }

        class TasksPack
        {
            public struct Task
            {
                public int Range { get; set; }
                public int Pack { get; set; }
                public int Exp { get; set; }
            }

            public List<Task> Tasks { get; private set; }

            public TasksPack()
            {
                Tasks = new List<Task>();
            }
        }

        enum CalcProgress
        {
            StartAll,
            RangePreCalc,
            RangePostCalc,
            PackPreCalc,
            PackPostCalc,
            ExpStartCalc,
            ExpFinishCalc,
            PackStartCalc,
            PackFinishCalc,
            FinishAll
        }

        struct CalculationState
        {
            public CalcProgress State { get; set; }
            public int Progress { get; set; }
            public int MaxProgress { get; set; }
            public TasksPack.Task Task { get; set; }
        }

        private void startCalculationThread(TasksPack calculateTask)
        {
            updateFormState(FormStateEnum.ShowProgressBar);
            m_backgroundWorker.RunWorkerAsync(calculateTask);
        }

        private void m_selectedExperimentToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (m_project == null)
                return;
            TreeNode selectedNode = m_treeView.SelectedNode;
            if (selectedNode == null)
                return;

            TreeNodeTag tag = (TreeNodeTag)selectedNode.Tag;

            if (tag.IsExperiment)
            {
                TasksPack calculateTask = new TasksPack();
                calculateTask.Tasks.Add(new TasksPack.Task { Range = tag.RangeNum, Pack = tag.PackNum, Exp = tag.ExpNum});
                startCalculationThread(calculateTask);
            }
        }

        private void m_selectedPackToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (m_project == null)
                return;
            
            TreeNode selectedNode = m_treeView.SelectedNode;
            if (selectedNode == null)
                return;

            TreeNodeTag tag = (TreeNodeTag)selectedNode.Tag;

            if (tag.IsPack)
            {
                TasksPack calculateTask = new TasksPack();
                for (int i = 0; i < tag.GetPack().Experiments.Count; ++i )
                    calculateTask.Tasks.Add(new TasksPack.Task { Range = tag.RangeNum, Pack = tag.PackNum, Exp = i});

                calculateTask.Tasks.Add(new TasksPack.Task { Range = tag.RangeNum, Pack = tag.PackNum, Exp = -1});
                startCalculationThread(calculateTask);
            }
            
        }

        private void m_selectedRangeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (m_project == null)
                return;

            TreeNode selectedNode = m_treeView.SelectedNode;
            if (selectedNode == null)
                return;

            TreeNodeTag tag = (TreeNodeTag)selectedNode.Tag;

            if (tag.IsRange)
            {
                TasksPack calculateTask = new TasksPack();
                for (int i = 0; i < tag.GetRange().Packs.Count; ++i)
                {
                    for (int j = 0; j < tag.GetRange().Packs[i].Experiments.Count; ++j )
                        calculateTask.Tasks.Add(new TasksPack.Task { Range = tag.RangeNum, Pack = i, Exp = j });

                    calculateTask.Tasks.Add(new TasksPack.Task { Range = tag.RangeNum, Pack = i, Exp = -1});
                }

                calculateTask.Tasks.Add(new TasksPack.Task { Range = tag.RangeNum, Pack = -1, Exp = -1});
                startCalculationThread(calculateTask);
            }
            
        }

        private void m_allRangesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (m_project == null)
                return;

            TasksPack calculateTask = new TasksPack();

            for (int i = 0; i < m_project.Ranges.Count; ++i)
            {
                for (int j = 0; j < m_project.Ranges[i].Packs.Count; ++j)
                {
                    for (int k = 0; k < m_project.Ranges[i].Packs[j].Experiments.Count; ++k)
                        calculateTask.Tasks.Add(new TasksPack.Task { Range = i, Pack = j, Exp = k });

                    calculateTask.Tasks.Add(new TasksPack.Task { Range = i, Pack = j, Exp = -1});
                }

                calculateTask.Tasks.Add(new TasksPack.Task { Range = i, Pack = -1, Exp = -1});
            }

            startCalculationThread(calculateTask);

        }


        private void m_backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            
            TasksPack tasksPack = (TasksPack)e.Argument;

            int progress = 0;
            int tasksCount = tasksPack.Tasks.Count;
            int maxProgress = tasksCount;

            m_backgroundWorker.ReportProgress(0, new CalculationState { State = 0, MaxProgress = maxProgress });

            tasksPack.Tasks.Where(x => x.Pack == -1 && x.Exp == -1).ToList().ForEach(task =>
                m_backgroundWorker.ReportProgress((progress / maxProgress) * 100, new CalculationState { Task = task, State = CalcProgress.RangePreCalc, Progress = progress})
            );

            tasksPack.Tasks.Where(x => x.Exp == -1 && x.Pack != -1).ToList().ForEach(task =>
                m_backgroundWorker.ReportProgress((progress / maxProgress) * 100, new CalculationState { Task = task, State = CalcProgress.PackPreCalc, Progress = progress})
            );

            System.Threading.Tasks.Parallel.ForEach(tasksPack.Tasks.Where(x => x.Exp != -1), task =>
            {
                int rangeNum = task.Range;
                int packNum = task.Pack;
                int expNum = task.Exp;

                Experiment exp = m_project.Ranges[rangeNum].Packs[packNum].Experiments[expNum];

                m_backgroundWorker.ReportProgress((progress / maxProgress) * 100, new CalculationState { Task = task, State = CalcProgress.ExpStartCalc, Progress = progress });

                if (exp.NetStats == null)
                    exp.addNetStatistics(NetProcessor.getStatistics(exp.Net));

                if (exp.HKStats == null)
                    exp.addHKStatistics(HoshenKopelmanNumbering.numberate(exp.Net));
                
                if (exp.BoltStats == null && m_project.Settings.getAlgorithms().ProcessBolt)
                    exp.addBoltStatistics(DijkstraSearch.search(exp.Net, m_project.Settings.getAlgorithms().BoltSettings));

                m_backgroundWorker.ReportProgress((++progress / maxProgress) * 100, new CalculationState { Task = task, State = CalcProgress.ExpFinishCalc, Progress = progress });
            }
            );

            System.Threading.Tasks.Parallel.ForEach(tasksPack.Tasks.Where(x => x.Exp == -1 && x.Pack != -1), task =>
                {
                    m_backgroundWorker.ReportProgress((progress / maxProgress) * 100, new CalculationState { Task = task, State = CalcProgress.PackStartCalc, Progress = progress });

                    m_project.Ranges[task.Range].Packs[task.Pack].calculatePackStatistics();
                    
                    m_backgroundWorker.ReportProgress((++progress / maxProgress) * 100, new CalculationState { Task = task, State = CalcProgress.PackFinishCalc, Progress = progress });
                }
            );

            tasksPack.Tasks.Where(x => x.Exp == -1 && x.Pack != -1).ToList().ForEach(task =>
                m_backgroundWorker.ReportProgress((progress / maxProgress) * 100, new CalculationState { Task = task, State = CalcProgress.PackPostCalc, Progress = progress })
            );

            tasksPack.Tasks.Where(x => x.Pack == -1 && x.Exp == -1).ToList().ForEach(task =>
                m_backgroundWorker.ReportProgress((++progress / maxProgress) * 100, new CalculationState { Task = task, State = CalcProgress.RangePostCalc, Progress = progress })
            );

            m_backgroundWorker.ReportProgress(100, new CalculationState { State = CalcProgress.FinishAll });
        }

        private void m_backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            
            CalculationState state = (CalculationState)e.UserState;
            if (state.State == CalcProgress.StartAll)
            {
                m_toolStripProgressBar.Minimum = 0;
                m_toolStripProgressBar.Value = 0;
                m_toolStripProgressBar.Maximum = state.MaxProgress;
                m_treeView.SelectedNode = null;
                updateFormState(FormStateEnum.HideForCalculating);
            }
            else if (state.State == CalcProgress.RangePreCalc)
            {
                findTreeNode(state.Task.Range, state.Task.Pack, state.Task.Exp).setIcon(TreeNodeIcon.Processing);
                m_toolStripStatusLabel.Text = "Task " + (state.Progress + 1).ToString() + " of " + m_toolStripProgressBar.Maximum.ToString();
            }
            else if (state.State == CalcProgress.RangePostCalc)
            {
                findTreeNode(state.Task.Range, state.Task.Pack, state.Task.Exp).setIcon(TreeNodeIcon.Processed);
                m_toolStripStatusLabel.Text = "Task " + (state.Progress + 1).ToString() + " of " + m_toolStripProgressBar.Maximum.ToString();
                ++m_toolStripProgressBar.Value;
            }
            else if (state.State == CalcProgress.PackPreCalc)
            {
                findTreeNode(state.Task.Range, state.Task.Pack, state.Task.Exp).setIcon(TreeNodeIcon.Processing);
                m_toolStripStatusLabel.Text = "Task " + (state.Progress + 1).ToString() + " of " + m_toolStripProgressBar.Maximum.ToString();
            }
            else if (state.State == CalcProgress.PackPostCalc)
            {
                findTreeNode(state.Task.Range, state.Task.Pack, state.Task.Exp).setIcon(TreeNodeIcon.Processed);
                m_toolStripStatusLabel.Text = "Task " + (state.Progress + 1).ToString() + " of " + m_toolStripProgressBar.Maximum.ToString();
            }
            else if (state.State == CalcProgress.ExpStartCalc)
            {
                findTreeNode(state.Task.Range, state.Task.Pack, state.Task.Exp).setIcon(TreeNodeIcon.Processing);

                m_toolStripStatusLabel.Text = "Task " + (state.Progress + 1).ToString() + " of " + m_toolStripProgressBar.Maximum.ToString();
            }
            else if (state.State == CalcProgress.ExpFinishCalc)
            {
                findTreeNode(state.Task.Range, state.Task.Pack, state.Task.Exp).setIcon(TreeNodeIcon.Processed);

                m_toolStripStatusLabel.Text = "Task " + (state.Progress + 1).ToString() + " of " + m_toolStripProgressBar.Maximum.ToString();
                ++m_toolStripProgressBar.Value;
            }
            else if (state.State == CalcProgress.PackStartCalc)
            {
                m_toolStripStatusLabel.Text = "Task " + (state.Progress + 1).ToString() + " of " + m_toolStripProgressBar.Maximum.ToString();
            }
            else if (state.State == CalcProgress.PackFinishCalc)
            {
                m_toolStripStatusLabel.Text = "Task " + (state.Progress + 1).ToString() + " of " + m_toolStripProgressBar.Maximum.ToString();
                ++m_toolStripProgressBar.Value;
            }
            else if (state.State == CalcProgress.FinishAll)
            {
                m_toolStripStatusLabel.Text = "";
                m_toolStripProgressBar.Visible = false;
            }
            
        }

        private void m_backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            updateFormState(FormStateEnum.HideProgressBar);
        }

        private KeyValuePair<NetPath, NetPathStatistics> getNetPathForDraw()
        {
            TreeNode selectedNode = m_treeView.SelectedNode;
            if (selectedNode == null)
                return new KeyValuePair<NetPath, NetPathStatistics>(null, null);

            TreeNodeTag tag = (TreeNodeTag)selectedNode.Tag;
            
            if (tag.IsExperiment && m_propertiesToolStripComboBox.SelectedIndex != -1 && m_previewPanelDrawMode == 0)
                return tag.GetExperiment().BoltStats.PathStatisticsList[m_propertiesToolStripComboBox.SelectedIndex];
            
            return new KeyValuePair<NetPath, NetPathStatistics>(null, null);
        }

        private void m_tabControl_SelectedIndexChanged(object sender, EventArgs e)
        {
            m_previewPanel.Invalidate();
        }

        private void m_fileToolStripSplitButton_ButtonClick(object sender, EventArgs e)
        {
            m_fileToolStripSplitButton.ShowDropDown();
        }

        private void m_applicationToolStripSplitButton_ButtonClick(object sender, EventArgs e)
        {
            m_applicationToolStripSplitButton.ShowDropDown();
        }

        private void m_projectToolStripSplitButton_ButtonClick(object sender, EventArgs e)
        {
            m_projectToolStripSplitButton.ShowDropDown();
        }

        private void m_calcToolStripSplitButton_ButtonClick(object sender, EventArgs e)
        {
            m_calcToolStripSplitButton.ShowDropDown();
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (m_project == null)
                return;

            m_saveFileDialog.Filter = "Percolation project files (*.ppf)|*.ppf";
            if (m_saveFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                BinaryFormatter serializer = new BinaryFormatter();
                using (FileStream file = new FileStream(m_saveFileDialog.FileName, FileMode.Create))
                {
                    serializer.Serialize(file, m_project);
                }
            }
        }

        private void closeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            m_project = null;
            updateFormState(FormStateEnum.Initial);
        }

        private void MainForm_Resize(object sender, EventArgs e)
        {
            Invalidate();
            m_previewPanel.Invalidate();
        }

        private void m_contentToolStripButton1_Click(object sender, EventArgs e)
        {
            if (m_selectedTab != 0)
            {
                m_selectedTab = 0;
                updateContentPane();

                TreeNode node = m_treeView.SelectedNode;
                if (node != null)
                {
                    TreeNodeTag tag = (TreeNodeTag)node.Tag;
                    if (tag.IsExperiment)
                    {
                        if (tag.GetExperiment().NetStats != null)
                            updateNetStats(tag.GetExperiment().NetStats);
                    }
                }

                m_previewPanel.Invalidate();
            }
        }

        private void m_contentToolStripButton2_Click(object sender, EventArgs e)
        {
            if (m_selectedTab != 1)
            {
                m_selectedTab = 1;
                updateContentPane();

                TreeNode node = m_treeView.SelectedNode;
                if (node != null)
                {
                    TreeNodeTag tag = (TreeNodeTag)node.Tag;
                    if (tag.IsExperiment)
                    {
                        if (tag.GetExperiment().HKStats != null)
                            updateHKStats(tag.GetExperiment().HKStats);
                    }
                }

                m_previewPanel.Invalidate();
            }
        }

        private void m_contentToolStripButton3_Click(object sender, EventArgs e)
        {
            if (m_selectedTab != 2)
            {
                m_selectedTab = 2;
                updateContentPane();

                TreeNode node = m_treeView.SelectedNode;
                if (node != null)
                {
                    TreeNodeTag tag = (TreeNodeTag)node.Tag;
                    if (tag.IsExperiment)
                    {
                        if (tag.GetExperiment().BoltStats != null)
                            updateBoltStats(tag.GetExperiment().BoltStats);
                    }
                }

                m_previewPanel.Invalidate();
            }
        }

        private void m_propertiesToolStripComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            TreeNode node = m_treeView.SelectedNode;
            if (node == null)
                return;

            TreeNodeTag tag = node.getTag();

            if (!tag.IsExperiment && !tag.IsRoot)
                return;

            if (tag.IsExperiment)
            {
                if (tag.GetExperiment().BoltStats == null)
                    return;

                updateBoltStats(tag.GetExperiment().BoltStats);
                m_previewPanelDrawMode = 0;
            }
            else
            {
                m_previewPanelDrawMode = 1;
            }

            m_previewPanel.Invalidate();
        }


        private TreeNode findTreeNode(int rangeNum, int packNum = -1, int expNum = -1)
        {
            if (m_project == null)
                return null;

            TreeNode root = m_treeView.Nodes[0];

            if (rangeNum == -1 && packNum == -1 && expNum == -1)
                return root;

            foreach (TreeNode rangeNode in root.Nodes)
            {
                TreeNodeTag rangeTag = (TreeNodeTag)rangeNode.Tag;

                if (rangeTag.RangeNum != rangeNum)
                    continue;

                if (packNum == -1 && expNum == -1)
                {
                    return rangeNode;
                }
                else
                {
                    foreach (TreeNode packNode in rangeNode.Nodes)
                    {
                        TreeNodeTag packTag = (TreeNodeTag)packNode.Tag;

                        if (packTag.PackNum != packNum)
                            continue;

                        if (expNum == -1)
                        {
                            return packNode;
                        }
                        else
                        {
                            foreach (TreeNode expNode in packNode.Nodes)
                            {
                                TreeNodeTag expTag = (TreeNodeTag)expNode.Tag;

                                if (expTag.ExpNum == expNum)
                                    return expNode;
                            }
                        }
                    }
                }
            }

            return null;
        }


        private void m_removeGraphicToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (m_project == null)
                return;

            int deleteIndex = m_propertiesToolStripComboBox.SelectedIndex;
            if (deleteIndex == -1)
                return;

            if (MessageBox.Show("Delete graphic?", "Percolation project", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes)
            {
                m_project.Graphics.RemoveAt(deleteIndex);
                updateContentPane();
                m_previewPanel.Invalidate();
            }
        }

        private void m_addGraphicToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (m_project == null)
                return;

            EditGraphicForm dialog = new EditGraphicForm(new Graphic(m_project, GraphicTypeEnum.PercolationProbabilityByImpurity, new List<int>()), m_project.Ranges.ToArray());
            dialog.ShowDialog();

            if (dialog.DialogResult == System.Windows.Forms.DialogResult.OK)
            {
                m_project.Graphics.Add(dialog.GetGraphic());
                m_propertiesToolStripComboBox.Items.Add("Graphic " + (m_propertiesToolStripComboBox.Items.Count+1).ToString());
                m_propertiesToolStripComboBox.SelectedIndex = m_propertiesToolStripComboBox.Items.Count - 1;
            }
        }

        private void m_actionsToolStripSplitButton_ButtonClick(object sender, EventArgs e)
        {
            m_actionsToolStripSplitButton.ShowDropDown();
        }

        [DllImportAttribute("gdi32.dll")]
        private static extern bool BitBlt(
        IntPtr hdcDest,
        int nXDest,
        int nYDest,
        int nWidth,
        int nHeight,
        IntPtr hdcSrc,
        int nXSrc,
        int nYSrc,
        int dwRop);

        public Bitmap CaptureControl(Control control)
        {
            Bitmap controlBmp;
            using (Graphics g1 = control.CreateGraphics())
            {
                controlBmp = new Bitmap(control.Width, control.Height, g1);
                using (Graphics g2 = Graphics.FromImage(controlBmp))
                {
                    IntPtr dc1 = g1.GetHdc();
                    IntPtr dc2 = g2.GetHdc();
                    BitBlt(dc2, 0, 0, control.Width, control.Height, dc1, 0, 0, 13369376);
                    g1.ReleaseHdc(dc1);
                    g2.ReleaseHdc(dc2);
                }
            }

            return controlBmp;
        }

        private void m_saveGraphicToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (m_project == null || m_propertiesToolStripComboBox.SelectedIndex == -1)
                return;

            Bitmap bitmap = CaptureControl(m_previewPanel);
            m_saveFileDialog.Filter = "Png files (*.png)|*.png";
            if (m_saveFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                bitmap.Save(m_saveFileDialog.FileName, ImageFormat.Png);
        }

        private void ExportToExcel(DataGridView dataGridView)
        {
            Microsoft.Office.Interop.Excel.Application ExcelApp = new Microsoft.Office.Interop.Excel.Application();
            Microsoft.Office.Interop.Excel.Workbook ExcelWorkBook;
            Microsoft.Office.Interop.Excel.Worksheet ExcelWorkSheet;

            ExcelWorkBook = ExcelApp.Workbooks.Add(System.Reflection.Missing.Value);

            ExcelWorkSheet = (Microsoft.Office.Interop.Excel.Worksheet)ExcelWorkBook.Worksheets.get_Item(1);

            bool addHeaderRow = !dataGridView.Columns.Cast<DataGridViewColumn>().All(x => x.HeaderCell.Value == "");

            for (int i = 0; i < dataGridView.Rows.Count + (addHeaderRow ? 1 : 0); ++i)
            {
                if (i == 0 && addHeaderRow)
                {
                    for (int j = 0; j < dataGridView.ColumnCount; ++j)
                        ExcelApp.Cells[i + 1, j + 1] = dataGridView.Columns[j].HeaderCell.Value;
                    continue;
                }

                for (int j = 0; j < dataGridView.ColumnCount; ++j)
                    ExcelApp.Cells[i + 1, j + 1] = dataGridView.Rows[addHeaderRow ? i - 1 : i].Cells[j].Value;
            }

            ExcelApp.Visible = true;
            ExcelApp.UserControl = true; 
        }

        private void m_exportToolStripButton_Click(object sender, EventArgs e)
        {
            ExportToExcel(m_statsDataGridView);
        }

        private void m_saveToolStripButton_Click(object sender, EventArgs e)
        {
            if (m_project == null)
                return;

            Bitmap bitmap = CaptureControl(m_previewPanel);
            m_saveFileDialog.Filter = "Png files (*.png)|*.png";
            if (m_saveFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                bitmap.Save(m_saveFileDialog.FileName, ImageFormat.Png);
        }

        private void m_contentSplitContainer_SplitterMoved(object sender, SplitterEventArgs e)
        {
            m_previewPanel.Invalidate();
        }
    }


    public static class Extensions2
    {
        public static MainForm.TreeNodeTag getTag(this TreeNode node)
        {
            return (MainForm.TreeNodeTag)node.Tag;
        }

        public static void setIcon(this TreeNode node, MainForm.TreeNodeIcon icon)
        {
            node.ImageIndex = (int) icon;
            node.SelectedImageIndex = (int) icon;
        }
    }

}
